#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>

using namespace std;

// 定义任务类
template <class T>
class task {
public:
    task(const T& e1, const T& e2) :_e1(e1), _e2(e2) {}

    virtual ~task() {}

    T run() const {
        return _e1 + _e2;
    }
    pair<T, T> get_args() const {
        return {_e1, _e2};
    }
private:
    T _e1, _e2;
};

// 3.0

// // 定义任务队列
// template <class T>
// class blocking_queue {
// public:

//     // 生产
//     void push(const T& e) {
//         pthread_mutex_lock(&_mutex);
//         // 如果发现队列满了，则无法继续生产
//         // 需要通知消费者来消费
//         if(_tq.size() == max_size) {
//             pthread_cond_wait(&p_cond, &_mutex);
//         }
//         // 到这里说明没满，或者被唤醒，本质也是没满
//         _tq.push(e);
//         //高于高水位线才通知消费者来消费
//         if(_tq.size() > high_level) {
//             pthread_cond_signal(&c_cond);
//         }
//         pthread_mutex_unlock(&_mutex);
//     }
    
//     // 消费
//     T pop() {
//         pthread_mutex_lock(&_mutex);
//         // 如果发现队列空了，则无法继续消费
//         // 需要通知生产者来生产
//         if(_tq.empty()) {
//             pthread_cond_wait(&c_cond, &_mutex);
//         }
//         // 到这里说明没空，或者被唤醒,，本质也是没空
//         T front = _tq.front();
//         _tq.pop();
//         //低于低水位线才通知生产者
//         if(_tq.size() < low_level) {
//             pthread_cond_signal(&p_cond);
//         }
//         pthread_mutex_unlock(&_mutex);
//         return front;
//     }

// private:
//     queue<T> _tq;
//     // 最多生产数
//     size_t max_size = 20;
//     pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
//     // 需要两个条件变量，一个用作消费者线程等待
//     // 另一个用作生产者等待
//     // 也可以使用一个，不过为了让意义更明确这里使用两个
//     pthread_cond_t c_cond = PTHREAD_COND_INITIALIZER;
//     pthread_cond_t p_cond = PTHREAD_COND_INITIALIZER;
//     int low_level = 5;
//     int high_level = 15;
// };

//2.0
template <class T>
class blocking_queue {
public:

    // 生产
    void push(const T& e) {
        pthread_mutex_lock(&_mutex);
        // 如果发现队列满了，则无法继续生产
        // 需要通知消费者来消费
        if(_tq.size() == max_size) {
            pthread_cond_wait(&p_cond, &_mutex);
        }
        // 到这里说明没满，或者被唤醒，本质也是没满
        _tq.push(e);
        //通知消费者
        pthread_cond_signal(&c_cond);
        pthread_mutex_unlock(&_mutex);
    }
    
    // 消费
    T pop() {
        pthread_mutex_lock(&_mutex);
        // 如果发现队列空了，则无法继续消费
        // 需要通知生产者来生产
        if(_tq.empty()) {
            pthread_cond_wait(&c_cond, &_mutex);
        }
        // 到这里说明没空，或者被唤醒,，本质也是没空
        T front = _tq.front();
        _tq.pop();
        //通知生产者
        pthread_cond_signal(&p_cond);
        pthread_mutex_unlock(&_mutex);
        return front;
    }

private:
    queue<T> _tq;
    // 最多生产数
    size_t max_size = 100;
    pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
    // 需要两个条件变量，一个用作消费者线程等待
    // 另一个用作生产者等待
    // 也可以使用一个，不过为了让意义更明确这里使用两个
    pthread_cond_t c_cond = PTHREAD_COND_INITIALIZER;
    pthread_cond_t p_cond = PTHREAD_COND_INITIALIZER;
};

// 1.0
// // 定义任务队列
// template <class T>
// class blocking_queue {
// public:

//     // 生产
//     void push(const T& e) {
//         pthread_mutex_lock(&_mutex);
//         // 如果发现队列满了，则无法继续生产
//         // 需要通知消费者来消费
//         if(_tq.size() == max_size) {
//             pthread_cond_wait(&_cond, &_mutex);
//         }
//         // 到这里说明没满，或者被唤醒，本质也是没满
//         _tq.push(e);
//         //通知消费者
//         pthread_cond_signal(&_cond);
//         pthread_mutex_unlock(&_mutex);
//     }
    
//     // 消费
//     T pop() {
//         pthread_mutex_lock(&_mutex);
//         // 如果发现队列空了，则无法继续消费
//         // 需要通知生产者来生产
//         if(_tq.empty()) {
//             pthread_cond_wait(&_cond, &_mutex);
//         }
//         // 到这里说明没空，或者被唤醒,，本质也是没空
//         T front = _tq.front();
//         _tq.pop();
//         //通知生产者
//         pthread_cond_signal(&_cond);
//         pthread_mutex_unlock(&_mutex);
//         return front;
//     }

// private:
//     queue<T> _tq;
//     // 最多生产数
//     size_t max_size = 10;
//     pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
//     pthread_cond_t _cond = PTHREAD_COND_INITIALIZER;
// };